﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;

namespace TemplateMigrator.Application
{
    public class Migrator
    {
        private void WalkDirectory(string path)
        {
            var info = new DirectoryInfo(path);

            // Directory
            var newPath = path
                .Replace(OriginalSourceTemplatePath, NewSolutionBasePath)
                .Replace(ProjectStringToReplace, ProjectReplacementString);

            if (!Directory.Exists(newPath))
            {
                if (IsValidDirectory(info.Name))
                {
                    Directory.CreateDirectory(newPath);
                }
            }

            foreach (var dir in info.GetDirectories())
            {
                if (IsValidDirectory(dir.Name))
                {
                    WalkDirectory(dir.FullName);
                }
            }

            foreach (var file in info.GetFiles())
            {
                var oldFilePath = file.FullName;
                var newFilePath = oldFilePath
                    .Replace(OriginalSourceTemplatePath, NewSolutionBasePath)
                    .Replace(ProjectStringToReplace, ProjectReplacementString);
                var extension = file.Extension;

                if (IsCodeFile(extension))
                {
                    WalkFile(oldFilePath, newFilePath);
                }

                else
                {
                    CopyFile(oldFilePath, newFilePath);
                }
            }
        }

        private bool IsValidDirectory(string directoryName)
        {
            switch (directoryName)
            {
                case "obj":
                case "bin":
                    return false;

                default:
                    return true;
            }
        }

        private bool IsCodeFile(string extension)
        {
            switch (extension)
            {
                case ".cs":
                case ".csproj":
                case ".sln":
                    return true;
                default:
                    return false;
            }
        }

        public void CopyFile(string path, string newPath)
        {
            File.Copy(path, newPath);
        }

        public void WalkFile(string path, string newPath)
        {
            var reader = new StreamReader(path);
            var writer = new StreamWriter(newPath);
            string line;
            string newLine;

            var pattern = @"\b[A-F0-9]{8}(?:-[A-F0-9]{4}){3}-[A-F0-9]{12}\b";
            var regex = new Regex(pattern, RegexOptions.IgnoreCase);

            while (null != (line = reader.ReadLine()))
            {
                if (ReplaceGuids)
                {
                    var matches = Regex.Matches(line, pattern);

                    foreach (var match in matches)
                    {
                        var oldGuid = match.ToString();
                        string newGuid;

                        if (_guidDictionary.ContainsKey(match.ToString()))
                        {
                            newGuid = _guidDictionary[oldGuid];
                        }
                        else
                        {
                            newGuid = Guid.NewGuid().ToString().ToUpper();
                            _guidDictionary.Add(oldGuid, newGuid);
                        }

                        line = line.Replace(oldGuid, newGuid);
                    }
                }

                if (line.Contains(ProjectStringToReplace))
                {
                    // Replace the string
                    newLine = line.Replace(ProjectStringToReplace, ProjectReplacementString);
                }
                else
                {
                    newLine = line;
                }

                writer.WriteLine(newLine);
            }

            writer.Dispose();
            reader.Dispose();
        }

        #region PROPERTIES

        /// <summary>
        ///     Folder that contains the template to copy
        /// </summary>
        public string OriginalSourceTemplatePath { get; set; }

        /// <summary>
        ///     Folder where you wish the new solution to go
        /// </summary>
        public string NewSolutionBasePath { get; set; }

        /// <summary>
        ///     String in the solution/project/namespace names that is replace
        /// </summary>
        public string ProjectStringToReplace { get; set; }

        /// <summary>
        ///     String in the solution/project/namespace names that replaces the original string (ProjectStringToReplace)
        /// </summary>
        public string ProjectReplacementString { get; set; }

        /// <summary>
        ///     Flag to determine whether to replace GUIDs in the solution or not
        /// </summary>
        /// <remarks>
        ///     Should always be set to true unless you are just making a copy to work out ideas
        /// </remarks>
        public bool ReplaceGuids { get; set; }

        /// <summary>
        ///     Dictionary of replacement guids
        /// </summary>
        private readonly Dictionary<string, string> _guidDictionary = new Dictionary<string, string>();

        #endregion // PROPERTIES

        #region CONSTRUCTORS

        /// <summary>
        ///     Base constructor
        /// </summary>
        /// <remarks>
        ///     If you use this constructor, you will have to supply the following
        ///     before running the application:
        ///     > OriginalSourceTemplatePath - path for template to copy
        ///     > NewSolutionBasePath - Path for the solution to exist under (this should be the companyName.Project path)
        ///     > ProjectStringToReplace - String in project and solution name that is replaced
        ///     > ProjectReplacementString - String to replace with
        /// </remarks>
        /// <example>
        ///     var migrator = new Migrator();
        ///     migrator.TemplateBasePath = @"c:\projects\Template";
        ///     migrator.NewSolutionBasePath = @"c:\projects\NewProject";
        ///     migrator.ProjectStringToReplace = "Template";
        ///     migrator.ProjectReplacementString = "CompanyName.Area.SolutionName";
        ///     // This is optional if you wish to replace GUIDs
        ///     migrator.ReplaceGuids = false;
        ///     // Run the application here
        /// </example>
        public Migrator()
        {
            ReplaceGuids = true;

            // Load Project Guids to avoid invalid solution files
            LoadProjectGuids();
        }

        private void LoadProjectGuids()
        {
            _guidDictionary.Add("603C0E0B-DB56-11DC-BE95-000D561079B0", "603C0E0B-DB56-11DC-BE95-000D561079B0");
            _guidDictionary.Add("F85E285D-A4E0-4152-9332-AB1D724D3325", "F85E285D-A4E0-4152-9332-AB1D724D3325");
            _guidDictionary.Add("E53F8FEA-EAE0-44A6-8774-FFD645390401", "E53F8FEA-EAE0-44A6-8774-FFD645390401");
            _guidDictionary.Add("E3E379DF-F4C6-4180-9B81-6769533ABE47", "E3E379DF-F4C6-4180-9B81-6769533ABE47");
            _guidDictionary.Add("FAE04EC0-301F-11D3-BF4B-00C04F79EFBC", "FAE04EC0-301F-11D3-BF4B-00C04F79EFBC");
            _guidDictionary.Add("8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942", "8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942");
            _guidDictionary.Add("A9ACE9BB-CECE-4E62-9AA4-C7E7C5BD2124", "A9ACE9BB-CECE-4E62-9AA4-C7E7C5BD2124");
            _guidDictionary.Add("4F174C21-8C12-11D0-8340-0000F80270F8", "4F174C21-8C12-11D0-8340-0000F80270F8");
            _guidDictionary.Add("3EA9E505-35AC-4774-B492-AD1749C4943A", "3EA9E505-35AC-4774-B492-AD1749C4943A");
            _guidDictionary.Add("06A35CCD-C46D-44D5-987B-CF40FF872267", "06A35CCD-C46D-44D5-987B-CF40FF872267");
            _guidDictionary.Add("978C614F-708E-4E1A-B201-565925725DBA", "978C614F-708E-4E1A-B201-565925725DBA");
            _guidDictionary.Add("AB322303-2255-48EF-A496-5904EB18DA55", "AB322303-2255-48EF-A496-5904EB18DA55");
            _guidDictionary.Add("F135691A-BF7E-435D-8960-F99683D2D49C", "F135691A-BF7E-435D-8960-F99683D2D49C");
            _guidDictionary.Add("BF6F8E12-879D-49E7-ADF0-5503146B24B8", "BF6F8E12-879D-49E7-ADF0-5503146B24B8");
            _guidDictionary.Add("F2A71F9B-5D33-465A-A702-920D77279786", "F2A71F9B-5D33-465A-A702-920D77279786");
            _guidDictionary.Add("E6FDF86B-F3D1-11D4-8576-0002A516ECE8", "E6FDF86B-F3D1-11D4-8576-0002A516ECE8");
            _guidDictionary.Add("20D4826A-C6FA-45DB-90F4-C717570B9F32", "20D4826A-C6FA-45DB-90F4-C717570B9F32");
            _guidDictionary.Add("CB4CE8C6-1BDB-4DC7-A4D3-65A1999772F8", "CB4CE8C6-1BDB-4DC7-A4D3-65A1999772F8");
            //_guidDictionary.Add("F85E285D-A4E0-4152-9332-AB1D724D3325", "F85E285D-A4E0-4152-9332-AB1D724D3325");
            //_guidDictionary.Add("E53F8FEA-EAE0-44A6-8774-FFD645390401", "E53F8FEA-EAE0-44A6-8774-FFD645390401");
            //_guidDictionary.Add("E3E379DF-F4C6-4180-9B81-6769533ABE47", "E3E379DF-F4C6-4180-9B81-6769533ABE47");
            _guidDictionary.Add("EFBA0AD7-5A72-4C68-AF49-83D382785DCF", "EFBA0AD7-5A72-4C68-AF49-83D382785DCF");
            _guidDictionary.Add("6BC8ED88-2882-458C-8E55-DFD12B67127B", "6BC8ED88-2882-458C-8E55-DFD12B67127B");
            _guidDictionary.Add("F5B4F3BC-B597-4E2B-B552-EF5D8A32436F", "F5B4F3BC-B597-4E2B-B552-EF5D8A32436F");
            _guidDictionary.Add("786C830F-07A1-408B-BD7F-6EE04809D6DB", "786C830F-07A1-408B-BD7F-6EE04809D6DB");
            _guidDictionary.Add("593B0543-81F6-4436-BA1E-4747859CAAE2", "593B0543-81F6-4436-BA1E-4747859CAAE2");
            _guidDictionary.Add("EC05E597-79D4-47f3-ADA0-324C4F7C7484", "EC05E597-79D4-47f3-ADA0-324C4F7C7484");
            _guidDictionary.Add("F8810EC1-6754-47FC-A15F-DFABD2E3FA90", "F8810EC1-6754-47FC-A15F-DFABD2E3FA90");
            _guidDictionary.Add("A1591282-1198-4647-A2B1-27E5FF5F6F3B", "A1591282-1198-4647-A2B1-27E5FF5F6F3B");
            _guidDictionary.Add("4D628B5B-2FBC-4AA6-8C16-197242AEB884", "4D628B5B-2FBC-4AA6-8C16-197242AEB884");
            _guidDictionary.Add("68B1623D-7FB9-47D8-8664-7ECEA3297D4F", "68B1623D-7FB9-47D8-8664-7ECEA3297D4F");
            _guidDictionary.Add("2150E333-8FDC-42A3-9474-1A3956D46DE8", "2150E333-8FDC-42A3-9474-1A3956D46DE8");
            _guidDictionary.Add("3AC096D0-A1C2-E12C-1390-A8335801FDAB", "3AC096D0-A1C2-E12C-1390-A8335801FDAB");
            _guidDictionary.Add("F184B08F-C81C-45F6-A57F-5ABD9991F28F", "F184B08F-C81C-45F6-A57F-5ABD9991F28F");
            _guidDictionary.Add("C252FEB5-A946-4202-B1D4-9916A0590387", "C252FEB5-A946-4202-B1D4-9916A0590387");
            _guidDictionary.Add("A860303F-1F3F-4691-B57E-529FC101A107", "A860303F-1F3F-4691-B57E-529FC101A107");
            _guidDictionary.Add("BAA0C2D2-18E2-41B9-852F-F413020CAA33", "BAA0C2D2-18E2-41B9-852F-F413020CAA33");
            _guidDictionary.Add("349C5851-65DF-11DA-9384-00065B846F21", "349C5851-65DF-11DA-9384-00065B846F21");
            _guidDictionary.Add("E24C65DC-7377-472B-9ABA-BC803B73C61A", "E24C65DC-7377-472B-9ABA-BC803B73C61A");
            //_guidDictionary.Add("FAE04EC0-301F-11D3-BF4B-00C04F79EFBC", "FAE04EC0-301F-11D3-BF4B-00C04F79EFBC");
            //_guidDictionary.Add("F184B08F-C81C-45F6-A57F-5ABD9991F28F", "F184B08F-C81C-45F6-A57F-5ABD9991F28F");
            //_guidDictionary.Add("8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942", "8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942");
            _guidDictionary.Add("3D9AD99F-2412-4246-B90B-4EAA41C64699", "3D9AD99F-2412-4246-B90B-4EAA41C64699");
            _guidDictionary.Add("76F1466A-8B6D-4E39-A767-685A06062A39", "76F1466A-8B6D-4E39-A767-685A06062A39");
            _guidDictionary.Add("C089C8C0-30E0-4E22-80C0-CE093F111A43", "C089C8C0-30E0-4E22-80C0-CE093F111A43");
            _guidDictionary.Add("DB03555F-0C8B-43BE-9FF9-57896B3C5E56", "DB03555F-0C8B-43BE-9FF9-57896B3C5E56");
            _guidDictionary.Add("60DC8134-EBA5-43B8-BCC9-BB4BC16C2548", "60DC8134-EBA5-43B8-BCC9-BB4BC16C2548");
            _guidDictionary.Add("BC8A1FFA-BEE3-4634-8014-F334798102B3", "BC8A1FFA-BEE3-4634-8014-F334798102B3");
            _guidDictionary.Add("14822709-B5A1-4724-98CA-57A101D1B079", "14822709-B5A1-4724-98CA-57A101D1B079");
            _guidDictionary.Add("D59BE175-2ED0-4C54-BE3D-CDAA9F3214C8", "D59BE175-2ED0-4C54-BE3D-CDAA9F3214C8");
            _guidDictionary.Add("32F31D43-81CC-4C15-9DE6-3FC5453562B6", "32F31D43-81CC-4C15-9DE6-3FC5453562B6");
            //_guidDictionary.Add("EFBA0AD7-5A72-4C68-AF49-83D382785DCF", "EFBA0AD7-5A72-4C68-AF49-83D382785DCF");
            //_guidDictionary.Add("6BC8ED88-2882-458C-8E55-DFD12B67127B", "6BC8ED88-2882-458C-8E55-DFD12B67127B");
            _guidDictionary.Add("6D335F3A-9D43-41b4-9D22-F6F17C4BE596", "6D335F3A-9D43-41b4-9D22-F6F17C4BE596");
            _guidDictionary.Add("2DF5C3F4-5A5F-47a9-8E94-23B4456F55E2", "2DF5C3F4-5A5F-47a9-8E94-23B4456F55E2");
            _guidDictionary.Add("D399B71A-8929-442a-A9AC-8BEC78BB2433", "D399B71A-8929-442a-A9AC-8BEC78BB2433");

        }

        /// <summary>
        ///     Constructor that allows setting of paths and replacement strings
        /// </summary>
        /// <param name="originalSourceTemplatePath">Base path of the template</param>
        /// <param name="newSolutionBasePath">Base path of the new solution</param>
        /// <param name="projectStringToReplace">Part of solution/project name that is replaced</param>
        /// <param name="projectReplacementString">New string that replaces the "ProjectStringToReplace" parameter</param>
        /// <param name="replaceGuids">Should GUIDs be replaced in the solution</param>
        /// <remarks>
        ///     Replacement string focuses on the part of the solution that is replaced, which means
        ///     it is the common part of the solution name that is replaced. See examples for more
        ///     information.
        /// </remarks>
        /// <example>
        ///     var migrator = new Migrator(@"c:\projects\Template", @"c:\projects\NewProject"
        ///     , "Template", "CompanyName.Area.SolutionName", false);
        ///     // Run the application here
        ///     Example 1: Solution set as a template
        ///     Solution name:  Template.sln
        ///     Project names:  Template.Application.csproj
        ///     Template.Domain.Models.csproj
        ///     Template.Present.Console.csproj
        ///     ProjectStringToReplace = "Template";
        ///     ProjectReplacementString = "CompanyName.Project";
        ///     Example 2: From previous company branded templarte
        ///     Solution name:  CompanyName.Project1.sln
        ///     Project names:  CompanyName.Project1.Application.csproj
        ///     CompanyName.Project1.Domain.Models.csproj
        ///     CompanyName.Project1.Present.Console.csproj
        ///     ProjectStringToReplace = "Project1";
        ///     ProjectReplacementString = "Project2";
        /// </example>
        public Migrator(string originalSourceTemplatePath, string newSolutionBasePath
            , string projectStringToReplace, string projectReplacementString, bool replaceGuids = true)
            : this()
        {
            OriginalSourceTemplatePath = originalSourceTemplatePath;
            NewSolutionBasePath = newSolutionBasePath;
            ProjectStringToReplace = projectStringToReplace;
            ProjectReplacementString = projectReplacementString;
            ReplaceGuids = replaceGuids;
        }

        #endregion // Constructors

        #region PUBLIC METHODS

        /// <summary>
        ///     Method used to migrate when
        /// </summary>
        public void MigrateTemplate()
        {
            // Verify migration variables are there
            if (string.IsNullOrEmpty(OriginalSourceTemplatePath) ||
                string.IsNullOrEmpty(NewSolutionBasePath) ||
                string.IsNullOrEmpty(ProjectStringToReplace) ||
                string.IsNullOrEmpty(ProjectReplacementString))
            {
                // Throw error
            }

            // Supplied to method to ensure recursion
            WalkDirectory(OriginalSourceTemplatePath);
        }

        /// <summary>
        ///     Migrate template with parameters for template and created solution
        /// </summary>
        /// <param name="originalSourceTemplatePath">Base path of the template</param>
        /// <param name="newSolutionBasePath">Base path of the new solution</param>
        /// <param name="projectStringToReplace">Part of solution/project name that is replaced</param>
        /// <param name="projectReplacementString">New string that replaces the "ProjectStringToReplace" parameter</param>
        /// <param name="replaceGuids">Should GUIDs be replaced in the solution</param>
        public void MigrateTemplate(string originalSourceTemplatePath, string newSolutionBasePath
            , string projectStringToReplace, string projectReplacementString, bool replaceGuids = true)
        {
            OriginalSourceTemplatePath = originalSourceTemplatePath;
            NewSolutionBasePath = newSolutionBasePath;
            ProjectStringToReplace = projectStringToReplace;
            ProjectReplacementString = projectReplacementString;
            ReplaceGuids = replaceGuids;

            MigrateTemplate();
        }

        #endregion // Public Methods
    }
}